package com.taoes.simpledocker.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.InspectContainerResponse;
import com.github.dockerjava.api.command.TopContainerResponse;
import com.github.dockerjava.api.model.Container;
import com.github.dockerjava.api.model.ContainerNetwork;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import com.taoes.simpledocker.controller.container.OperateContainerRequest;
import com.taoes.simpledocker.controller.container.RunNewContainerRequest;
import com.taoes.simpledocker.controller.network.OperateNetworkRequest;
import com.taoes.simpledocker.model.ResponseModel;
import com.taoes.simpledocker.model.Role;
import com.taoes.simpledocker.model.enums.ContainerOperate;
import com.taoes.simpledocker.service.ContainerService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 容器相关服务
 *
 * @author lfs
 * @date 2021/12/4 11:35 下午
 */
@Slf4j
@RestController
@RequestMapping("/api/container")
@ApiSort(value = 4)
@Api(tags = "Docker容器管理")
@RequiredArgsConstructor
public class ContainerController extends BaseController {

    private final ContainerService service;


    @PostMapping("/rename")
    @ApiOperation("重命名容器")
    @SaCheckPermission(value = "container:rename", orRole = Role.ADMIN_ROLE_NAME)
    public ResponseModel<Boolean> rename(
            @RequestBody OperateContainerRequest request) {
        String containerId = request.getContainerId();
        String newName = request.getProperties().get("newName");
        if (StrUtil.isBlank(newName)) {
            return ResponseModel.fail("重命名失败");
        }
        service.rename(containerId, newName);
        return ResponseModel.ok(Boolean.TRUE);
    }

    @GetMapping
    @ApiOperation("容器列表")
    @SaCheckPermission(value = "container:query", orRole = Role.ADMIN_ROLE_NAME)
    public ResponseModel<List<Container>> list() {
        final List<Container> containers = service.list(true);
        return ResponseModel.ok(containers);
    }

    @ApiOperation(value = "清理容器",notes = "清除所有未启动的容器")
    @SaCheckPermission(value = "image:prune", orRole = Role.ADMIN_ROLE_NAME)
    @PostMapping("/prune")
    public ResponseModel<Boolean> purge() {
        service.prune();
        return ResponseModel.ok(Boolean.TRUE);
    }

    @GetMapping("/{containerId}")
    @ApiOperation("容器详情信息")
    @SaCheckPermission(value = "container:query", orRole = Role.ADMIN_ROLE_NAME)
    public InspectContainerResponse inspect(@PathVariable String containerId) {
        return service.inspect(containerId);
    }

    @ApiOperation(value = "运行新容器", notes = "简单模式/专业模式，pro参数区分，默认为false，简单模式")
    @PostMapping("/new")
    @SaCheckPermission(value = "container:run", orRole = Role.ADMIN_ROLE_NAME)
    public CreateContainerResponse run(@RequestBody RunNewContainerRequest request) {
        return service.run(request);
    }

    @ApiOperation("查看容器进程")
    @SaCheckPermission(value = "container:runtime", orRole = Role.ADMIN_ROLE_NAME)
    @GetMapping("/{containerId}/top")
    public TopContainerResponse top(@PathVariable String containerId, String psArgs) {
        return service.top(containerId, psArgs);
    }

    @ApiOperation(value = "变更容器状态")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "operate", value = "操作容器", required = true),
            @ApiImplicitParam(name = "request", value = "操作容器参数", required = false)})
    @SaCheckPermission(value = "container:operator", orRole = Role.ADMIN_ROLE_NAME)
    @PostMapping("/operator/{operate}")
    public ResponseModel<Boolean> operateContainer(
            @PathVariable ContainerOperate operate,
            @RequestBody OperateContainerRequest request) {

        final String containerId = request.getContainerId();
        final Map<String, String> properties = request.findProperties();
        final List<String> authentication = getAuthentication();
        try {
            switch (operate) {
                case START:
                    service.start(containerId);
                    break;
                case STOP:
                    service.stop(containerId);
                    break;
                case PAUSE:
                    service.pause(containerId);
                    break;
                case UNPAUSE:
                    service.unpause(containerId);
                    break;
                case RESTART:
                    service.restart(containerId);
                    break;
                case REMOVE:
                    service.remove(containerId, properties);
                    break;
                case EXPORT_LOCAL:
                    service.exportAfterCommit(containerId);
                default:
            }
        } catch (Exception e) {
            throw new RuntimeException("操作失败:" + e.getMessage());
        }
        return ResponseModel.ok(Boolean.TRUE);
    }

    @ApiOperation(value = "网络管理：断开指定的网络连接")
    @SaCheckPermission(value = "container:runtime", orRole = Role.ADMIN_ROLE_NAME)
    @PostMapping("/network/disconnect")
    public ResponseModel<Boolean> disconnectFromNetwork(@RequestBody OperateNetworkRequest request) {
        String containerId = request.getContainerId();
        String networkId = request.getNetworkId();
        try {
            service.disconnectFromNetwork(containerId, networkId);
        } catch (Exception e) {
            throw new RuntimeException("断开网络连接失败:" + e.getMessage());
        }
        return ResponseModel.ok(Boolean.TRUE);
    }

    @ApiOperation(value = "网络管理：连接到指定的网络",notes = "不能连接到host网络，连接到none网络则不能连接到其他网络，否则会导致服务器崩溃")
    @SaCheckPermission(value = "container:runtime", orRole = Role.ADMIN_ROLE_NAME)
    @PostMapping("/network/connectToNetwork")
    public ResponseModel connectToNetwork(@RequestBody OperateNetworkRequest request) {
        String containerId = request.getContainerId();
        String networkId = request.getNetworkId();
        Map<String, ContainerNetwork> networks = service.inspect(containerId).getNetworkSettings().getNetworks();
        Set<String> keySet = networks.keySet();
        if (!keySet.isEmpty()) {
            if (keySet.toString().contains("none")||networkId.contains("none")) {
                //    return ResponseModel.fail("Error response from daemon: container cannot be connected to multiple networks with one of the networks in private (none) mode");
                return ResponseModel.ok("Error response from daemon: container cannot be connected to multiple networks with one of the networks in private (none) mode");
            }

        }
        if (networkId.contains("host")) {
            //   return ResponseModel.fail("Error response from daemon: container cannot be disconnected from host network or connected to host network");
            return ResponseModel.ok("Error response from daemon: container cannot be disconnected from host network or connected to host network");
        }
        try {
            service.connectToNetwork(containerId, networkId);
        } catch (Exception e) {
            throw new RuntimeException("网络连接失败:" + e.getMessage());
        }
        return ResponseModel.ok(Boolean.TRUE);
    }

    @ApiOperation("下载文件")
    @SaCheckPermission(value = "container:download", orRole = Role.ADMIN_ROLE_NAME)
    @PostMapping("/file/download")
    public void save(@RequestParam String containerId, @RequestParam String filePath
            , HttpServletResponse response) {
        try (InputStream input = service.copyFileFromContainer(containerId, filePath);
             ServletOutputStream output = response.getOutputStream()) {
            String[] split = filePath.split("//");
            response.setContentType("application/x-tar;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + split[0] + ".tar");
            // 循环取出流中的数据
            byte[] b = new byte[1024];
            int len;
            while ((len = input.read(b)) > 0) {
                output.write(b, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @GetMapping("/network/{containerId}")
    @ApiOperation("容器网络连接信息")
    @SaCheckPermission(value = "container:query", orRole = Role.ADMIN_ROLE_NAME)
    public Set<String> inspectNetwork(@PathVariable String containerId) {
        Map<String, ContainerNetwork> networks = service.inspect(containerId).getNetworkSettings().getNetworks();
        if (networks.isEmpty()) {
            return null;
        }
        Set<String> strings = networks.keySet();

        return strings;
    }

    @ApiOperation(value = "导出容器",notes = "先将容器快照生成为镜像后下载（生成的镜像tag为：containerId:new,按需求之后再重新标记）")
    @SaCheckPermission(value = "container:export", orRole = Role.ADMIN_ROLE_NAME)
    @GetMapping("/save/{containerId}")
    public void save(@PathVariable String containerId, HttpServletResponse response) {
        try (InputStream input = service.exportAfterCommit(containerId);
             ServletOutputStream output = response.getOutputStream()) {
            String currentTime = DateUtil.format(DateUtil.date(), "yyyyMMdd_HHmmss_");
            response.setContentType("application/x-tar;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + currentTime + containerId + ".tar");
            // 循环取出流中的数据
            byte[] b = new byte[1024];
            int len;
            while ((len = input.read(b)) > 0) {
                output.write(b, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}